﻿using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Windows;
using System.Windows.Resources;
using System.Xml.Linq;

namespace Odin.Silverlight
{
	public class AssemblyLoadEventArgs : EventArgs
	{
		public Assembly Assembly { get; set; }

	}

	public class ElementLoadedEventArgs : EventArgs
	{
		public object Element { get; set; }
	}

	public static class SubApplicationHelper
	{
		public static void LoadTestAssemblyFromXapAsync(string xapName, EventHandler<AssemblyLoadEventArgs> callback)
		{
			WebClient client = new WebClient();

			client.OpenReadCompleted += (sender, e) =>
			{
				var sri = new StreamResourceInfo(e.Result, null);
				var sri2 = Application.GetResourceStream(sri,
														 new Uri("AppManifest.xaml", UriKind.Relative));
				var streamReader = new StreamReader(sri2.Stream);
				var xaml = streamReader.ReadToEnd();
				XDocument document = XDocument.Parse(xaml);
				XElement root = document.Root;
				if (null != root)
				{
					Assembly entryAssembly = null;

					var entryPoint = root.Attribute("EntryPointAssembly").Value;
					var partsElement = root.FirstNode as XElement;
					if (null != partsElement)
					{
						var parts = from p in partsElement.Elements()
									select p.Attribute("Source").Value;

						foreach (var part in parts)
						{
							AssemblyPart assemblyPart = new AssemblyPart();
							assemblyPart.Source = part;

							StreamResourceInfo info = Application.GetResourceStream(
								new StreamResourceInfo(e.Result, "application/binary"),
								new Uri(assemblyPart.Source, UriKind.Relative));

							var asm = assemblyPart.Load(info.Stream);
							if (part.Contains(entryPoint))
							{
								entryAssembly = asm;
							}
						}
					}

					callback(client, new AssemblyLoadEventArgs { Assembly = entryAssembly });
				}
			};

			client.OpenReadAsync(new Uri(xapName, UriKind.Relative));
		}

		public static void GetElementFromXap(string xapName,
			string type, EventHandler<ElementLoadedEventArgs> elementLoaded)
		{
			WebClient client = new WebClient();
			client.OpenReadCompleted += (sender, e) =>
			{

				string dll = Path.GetFileNameWithoutExtension(xapName) + ".dll";
				try
				{
					var element = LoadElementFromXap(dll, type, e.Result);
					var eventArgs = new ElementLoadedEventArgs { Element = element };
					elementLoaded(client, eventArgs);
				}
				finally
				{
				}
			};
			client.OpenReadAsync(new Uri(xapName, UriKind.Relative));
		}

		public static object LoadElementFromXap(string dllFile, string type, Stream stream)
		{
			string manifest = new StreamReader(
				Application.GetResourceStream(
					new StreamResourceInfo(
						stream, null),
					new Uri("AppManifest.xaml", UriKind.Relative)).Stream).ReadToEnd();


			var document = XDocument.Parse(manifest);
			var parts = document.Root.Nodes().ToArray()[0] as XElement;

			Assembly asm = null;
			foreach (XElement node in parts.Nodes().ToArray())
			{
				var source = node.Attribute("Source").Value;

				var part = new AssemblyPart();


				StreamResourceInfo info = Application.GetResourceStream(
					new StreamResourceInfo(stream, "application/binary"),
					new Uri(source, UriKind.Relative));

				if (source == dllFile)
				{
					asm = part.Load(info.Stream);
				}
				else
				{
					part.Load(info.Stream);
				}
			}

			if (null != asm)
			{
				Type uiType = (from t in asm.GetTypes()
							   where t.Name.Contains(type)
							   select t).Single();
				if (null != uiType)
				{
					object element = asm.CreateInstance(uiType.FullName);
					return element;
				}
			}
			return null;

		}
	}
}
